/**
 * @ Author: luoqi
 * @ Create Time: 2024-03-14 10:53
 * @ Modified by: luoqi
 * @ Modified time: 2025-05-18 23:30
 * @ Description:
 */

#ifndef __QLINK_H
#define __QLINK_H

#include <stdint.h>

#ifndef QNULL
#define QNULL ((void *)0)
#endif

#ifndef size_t
typedef unsigned int size_t;
#endif

typedef struct _qlink_list {
    struct _qlink_list *prev;
    struct _qlink_list *next;
} QLinkList;

#define QLINK_ENTRY(ptr, type, member) ((type *)((char *)(ptr) - ((unsigned long) &((type*)0)->member)))
#define QLINK_ITERATOR(node, list) for (node = (list)->next; node != (list); node = node->next)
#define QLINK_ITERATOR_SAFE(node, cache, list) for (node = (list)->next, cache = node->next; node != (list); node = cache, cache = node->next)

typedef enum {
    QLINK_UNPACK_ECRC = -3,
    QLINK_UNPACK_EACK = -2,
    QLINK_UNPACK_ERROR = -1,
    QLINK_UNPACK_OK = 0,
    QLINK_UNPACK_NOTSUBSCRIBE = 1,
    QLINK_UNPACK_DECODING = 2,
}  QLinkUnpackStatus;

typedef enum {
    QLINK_NACK = 0,
    QLINK_ACK,
} QLinkIsAck;

typedef struct qlink_topic {
    const char *name;                       // Name of the topic
    uint16_t id;                            // Unique identifier for the topic
    QLinkIsAck isack;                       // Acknowledgment status
    int (*callback)(void *args, int size);  // Callback function for the topic

    QLinkList sub_node;               // Linked list node for topic subscription
} QLinkTopic;

typedef struct {
    const char *name;                       // Name of the acknowledgment
    uint16_t id;                            // Unique identifier for the acknowledgment
    uint32_t nack_cnt;                       // Current count of no acknowledgments
    uint32_t nack_max;                       // Maximum allowed no acknowledgments
    uint32_t retry;                         // Number of retries for acknowledgment
    uint32_t retry_cnt;                     // Current retry count
    uint8_t *buf;                         // Cache to store data for the publish topic
    uint8_t bufsz;                     // Size of the cache
    void (*timeout_cb)(void);               // Timeout callback function

    QLinkList ack_node;                     // Linked list node for acknowledgment
} QLinkAck;

typedef struct qlink {
    uint8_t *dbuf;                          // Data buffer for incoming packets
    uint16_t recvcnt;                       // Count of received bytes
    uint16_t crc;                           // Cyclic Redundancy Check value
    uint8_t stamp;                          // Timestamp for the packet;

    uint32_t rpackets;                      // Total number of received packets
    uint32_t epackets;                      // Total number of error packets
    uint32_t transmissions;                 // Total number of transmitted packets

    int (*send)(const uint8_t *data, int size);  // Send function pointer
    int (*recv)(uint8_t *buf, int size);         // Receive function pointer

    QLinkList sub_list;               // Linked list of subscribed topics
    QLinkList ack_list;               // Linked list of topics needing acknowledgment
    uint32_t topic_num;               // Number of subscribed topics
    uint32_t ack_num;                 // Number of acknowledgments topicss
} QLink;

/**
 * Initialize the QLink structure.
 *
 * @param link Pointer to the QLink structure to be initialized.
 * @param dbuf Pointer to the data buffer for incoming packets.
 * @param send Function pointer for sending data.
 * @param recv Function pointer for receiving data.
 * @return 0 on success, non-zero on failure.
 */
int qlink_init(QLink *link, uint8_t *dbuf, int(*send)(const uint8_t *, int), int(*recv)(uint8_t *, int));

/**
 * Subscribe to a topic.
 *
 * @param link Pointer to the QLink structure.
 * @param topic Pointer to the QLinkTopic structure to be subscribed.
 * @param name Name of the topic to subscribe to.
 * @param cb Callback function for the topic.
 * @return 0 on success, non-zero on failure.
 */
int qlink_sub(QLink *link, QLinkTopic *topic, const char *name, int (*cb)(void *, int), QLinkIsAck isack);

/**
 * Publish data to a topic.
 *
 * @param link Pointer to the QLink structure.
 * @param buf Buffer to store the packed data.
 * @param topic Name of the topic to publish to.
 * @param data Data to be published.
 * @param size Size of the data to be published.
 * @return 0 on success, non-zero on failure.
 */
int qlink_pub(QLink *link, uint8_t *buf, const char *topic, const uint8_t *data, uint8_t size);

/**
 * Initialize the QLinkAck structure.
 *
 * @param ack Pointer to the QLinkAck structure to be initialized.
 * @param name Name of the acknowledgment.
 * @param timeout Maximum allowed negative acknowledgments.
 * @param timeout_cb Timeout callback function.
 * @param buf Cache to store data for the publish topic.
 * @param bufsz Size of the cache.
 * @return 0 on success, non-zero on failure.
 */
int qlink_ack_init(QLinkAck *ack, const char *topic, uint32_t timeout, uint32_t retry, void (*timeout_cb)(void), uint8_t *buf, uint8_t bufsz);

/**
 * Execute acknowledgment processing.
 *
 * @param link Pointer to the QLink structure.
 * @param buf Buffer containing the acknowledgment data.
 * @return 0 on success, non-zero on failure.
 */
int qlink_ack(QLink *link, uint8_t *buf);

/**
 * Publish data to a topic with acknowledgment.
 *
 * @param link Pointer to the QLink structure.
 * @param buf Buffer to store the packed data.
 * @param topic Name of the topic to publish to.
 * @param data Data to be published.
 * @param size Size of the data to be published.
 * @param ack Pointer to the QLinkAck structure for acknowledgment.
 * @return 0 on success, non-zero on failure.
 */
int qlink_pub_ack(QLink *link, uint8_t *buf, const char *topic, const uint8_t *data, uint8_t size, QLinkAck *ack);

/**
 * Set the acknowledgment status for a topic.
 *
 * @param link Pointer to the QLink structure.
 * @param topic Name of the topic.
 * @param isack Acknowledgment status.
 * @return 0 on success, non-zero on failure.
 */
int qlink_sub_isack(QLink *link, const char *topic, QLinkIsAck isack);

/**
 * Pack data into a buffer with topic information.
 *
 * @param buf Buffer to store the packed data.
 * @param topic Name of the topic.
 * @param data Data to be packed.
 * @param size Size of the data to be packed.
 * @return Size of the packed data.
 */
int qlink_pack(uint8_t *buf, const char *topic, const uint8_t *data, uint32_t size, uint8_t stamp);

/**
 * Unpack data from a buffer and execute the callback function.
 *
 * @param link Pointer to the QLink structure.
 * @param buf Buffer containing the data to be unpacked.
 * @param size Size of the data to be unpacked.
 * @return 0 on success, QLINK_DECODEING on decoding.
 */
QLinkUnpackStatus qlink_unpack(QLink *link, const uint8_t *data, uint32_t size, int *cb_status, uint8_t *stamp);


/**
 * Unpack data from a buffer without executing the callback function.
 *
 * @param link Pointer to the QLink structure.
 * @param data Buffer containing the data to be unpacked.
 * @param size Size of the data to be unpacked.
 * @param id Pointer to store the unpacked topic ID.
 * @param unpacked Pointer to store the unpacked data.
 * @param rsz Pointer to store the size of the unpacked data.
 * @param stamp Pointer to store the timestamp of the unpacked data.
 * @return QLinkUnpackStatus indicating the result of the unpacking process.
 */
QLinkUnpackStatus qlink_unpack_only(QLink *link, const uint8_t *data, uint32_t size, uint16_t *id, uint8_t *unpacked, uint8_t *rsz, uint8_t *stamp);

/**
 * Get the total number of received packets.
 *
 * @param link Pointer to the QLink structure.
 * @return Total number of received packets.
 */
uint32_t qlink_rpackets(QLink *link);

/**
 * Get the total number of error packets.
 *
 * @param link Pointer to the QLink structure.
 * @return Total number of error packets.
 */
uint32_t qlink_epackets(QLink *link);

/**
 * Get the total number of transmitted packets.
 *
 * @param link Pointer to the QLink structure.
 * @return Total number of transmitted packets.
 */
uint32_t qlink_transmissions(QLink *link);

#endif
